/*
 * @(#)CardFigure.java  1.0  18. Juni 2006
 *
 * Copyright (c) 1996-2006 by the original authors of JHotDraw
 * and all its contributors ("JHotDraw.org")
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JHotDraw.org ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only in accordance
 * with the terms of the license agreement you entered into with
 * JHotDraw.org.
 */

package com.thoughtworks.studio.tools.cardkit.gui.figures;

import static org.jhotdraw.draw.AttributeKeys.FILL_COLOR;
import static org.jhotdraw.draw.AttributeKeys.FONT_BOLD;
import static org.jhotdraw.draw.AttributeKeys.STROKE_COLOR;
import static org.jhotdraw.draw.AttributeKeys.STROKE_DASHES;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jhotdraw.draw.AbstractAttributedFigure;
import org.jhotdraw.draw.AttributeKey;
import org.jhotdraw.draw.ConnectorHandle;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.FigureAdapter;
import org.jhotdraw.draw.FigureEvent;
import org.jhotdraw.draw.GraphicalCompositeFigure;
import org.jhotdraw.draw.Handle;
import org.jhotdraw.draw.ListFigure;
import org.jhotdraw.draw.LocatorConnector;
import org.jhotdraw.draw.MoveHandle;
import org.jhotdraw.draw.RectangleFigure;
import org.jhotdraw.draw.RelativeLocator;
import org.jhotdraw.draw.TextAreaFigure;
import org.jhotdraw.draw.TextFigure;
import org.jhotdraw.draw.VerticalLayouter;
import org.jhotdraw.geom.Insets2D;
import org.jhotdraw.util.ResourceBundleUtil;
import org.jhotdraw.xml.DOMInput;
import org.jhotdraw.xml.DOMOutput;

/**
 * CardFigure.
 */
public class CardFigure extends GraphicalCompositeFigure {
	/**
	 * 
	 */
	private static final long serialVersionUID = 5601801115752984954L;
	private HashSet<DependencyFigure> dependencies;

	/**
	 * This adapter is used, to connect a TextFigure with the number of the
	 * CardFigure model.
	 */
	private static class NumberAdapter extends FigureAdapter {
		private CardFigure target;

		public NumberAdapter(CardFigure target) {
			this.target = target;
		}

		public void figureAttributeChanged(FigureEvent e) {
			// We could fire a property change event here, in case
			// some other object would like to observe us.
			// target.firePropertyChange("name", e.getOldValue(),
			// e.getNewValue());
		}
	}

	private static class SummaryAdapter extends FigureAdapter {
		private CardFigure target;

		public SummaryAdapter(CardFigure target) {
			this.target = target;
		}

		public void figureAttributeChanged(FigureEvent evt) {
			// We could fire a property change event here, in case
			// some other object would like to observe us.
			// target.firePropertyChange("duration", e.getOldValue(),
			// e.getNewValue());
		}
	}

	/** Creates a new instance. */
	public CardFigure() {
		super(new RectangleFigure());

		setLayouter(new VerticalLayouter());

		RectangleFigure cardCompartmentPF = new RectangleFigure();
		STROKE_COLOR.basicSet(cardCompartmentPF, null);
		cardCompartmentPF.setAttributeEnabled(STROKE_COLOR, false);
		FILL_COLOR.basicSet(cardCompartmentPF, null);
		cardCompartmentPF.setAttributeEnabled(FILL_COLOR, false);
		ListFigure cardCompartment = new ListFigure(cardCompartmentPF);
		ListFigure attributeCompartment = new ListFigure();

		applyAttributes(getPresentationFigure());

		add(cardCompartment);
		add(attributeCompartment);

		Insets2D.Double insets = new Insets2D.Double(4, 8, 4, 8);
		LAYOUT_INSETS.basicSet(cardCompartment, insets);
		LAYOUT_INSETS.basicSet(attributeCompartment, insets);

		TextFigure numberFigure;
		cardCompartment.add(numberFigure = new TextFigure("999"));
		FONT_BOLD.basicSet(numberFigure, true);
		numberFigure.setAttributeEnabled(FONT_BOLD, false);

		TextAreaFigure summaryFigure;
		attributeCompartment.add(summaryFigure = new TextAreaFigure());
		FONT_BOLD.basicSet(summaryFigure, true);
		summaryFigure.setBounds(new Double(0,0,95,65));
		summaryFigure.setEditable(false);
		summaryFigure.setFontSize(9);
		summaryFigure.setText("card name");
		applyAttributes(this);
		setAttributeEnabled(STROKE_DASHES, false);

		ResourceBundleUtil labels = ResourceBundleUtil
				.getLAFBundle("com.thoughtworks.studio.tools.cardkit.gui.Labels");

		setNumber(labels.getString("cardNumber"));
		setSummary("card name");

		dependencies = new HashSet<DependencyFigure>();
		numberFigure.addFigureListener(new NumberAdapter(this));
		summaryFigure.addFigureListener(new SummaryAdapter(this));
	}

	public Collection<Handle> createHandles(int detailLevel) {
		List<Handle> handles = new LinkedList<Handle>();
		if (detailLevel == 0) {
			handles.add(new MoveHandle(this, RelativeLocator.northWest()));
			handles.add(new MoveHandle(this, RelativeLocator.northEast()));
			handles.add(new MoveHandle(this, RelativeLocator.southWest()));
			handles.add(new MoveHandle(this, RelativeLocator.southEast()));
			handles.add(new ConnectorHandle(new LocatorConnector(this,
					RelativeLocator.east()), new DependencyFigure()));
		}
		return handles;
	}

	public void setNumber(String newValue) {
		getNumberFigure().setText(newValue);
	}

	public String getNumber() {
		return getNumberFigure().getText();
	}

	public void setSummary(String newValue) {
		String oldValue = getDuration();
		getSummaryFigure().setText(newValue);
	}

	public String getDuration() {
		try {
			return getSummaryFigure().getText();
		} catch (NumberFormatException e) {
			return "";
		}
	}


	public String getDescription() {
		return getDescriptionFigure().getText();
	}

	private TextFigure getNumberFigure() {
		return (TextFigure) ((ListFigure) getChild(0)).getChild(0);
	}

	private TextAreaFigure getSummaryFigure() {
		return (TextAreaFigure) ((ListFigure) getChild(1)).getChild(0);
	}

	private TextFigure getDescriptionFigure() {
		return (TextFigure) ((ListFigure) getChild(1)).getChild(1);
	}

	private void applyAttributes(Figure f) {
		Map<AttributeKey, Object> attr = ((AbstractAttributedFigure) getPresentationFigure())
				.getAttributes();
		for (Map.Entry<AttributeKey, Object> entry : attr.entrySet()) {
			entry.getKey().basicSet(f, entry.getValue());
		}
	}

	public CardFigure clone() {
		CardFigure that = (CardFigure) super.clone();
		that.dependencies = new HashSet<DependencyFigure>();
		that.getNumberFigure().addFigureListener(new NumberAdapter(that));
		that.getSummaryFigure().addFigureListener(new SummaryAdapter(that));
		return that;
	}

	public void read(DOMInput in) throws IOException {
		double x = in.getAttribute("x", 0d);
		double y = in.getAttribute("y", 0d);
		double w = in.getAttribute("w", 0d);
		double h = in.getAttribute("h", 0d);
		setBounds(new Point2D.Double(x, y), new Point2D.Double(x + w, y + h));
		readAttributes(in);
		in.openElement("model");
		in.openElement("name");
		setNumber((String) in.readObject());
		in.closeElement();
		in.openElement("duration");
		setSummary((String) in.readObject());
		in.closeElement();
		in.closeElement();
	}

	public void write(DOMOutput out) throws IOException {
		Rectangle2D.Double r = getBounds();
		out.addAttribute("x", r.x);
		out.addAttribute("y", r.y);
		writeAttributes(out);
		out.openElement("model");
		out.openElement("name");
		out.writeObject(getNumber());
		out.closeElement();
		out.openElement("duration");
		out.writeObject(getDuration());
		out.closeElement();
		out.closeElement();
	}

	public int getLayer() {
		return 0;
	}

	public Set<DependencyFigure> getDependencies() {
		return Collections.unmodifiableSet(dependencies);
	}

	public void addDependency(DependencyFigure f) {
		dependencies.add(f);
	}

	public void removeDependency(DependencyFigure f) {
		dependencies.remove(f);
	}

	/**
	 * Returns dependent PertTasks which are directly connected via a
	 * PertDependency to this CardFigure.
	 */
	public List<CardFigure> getSuccessors() {
		LinkedList<CardFigure> list = new LinkedList<CardFigure>();
		for (DependencyFigure c : getDependencies()) {
			if (c.getStartFigure() == this) {
				list.add((CardFigure) c.getEndFigure());
			}
		}
		return list;
	}

	/**
	 * Returns predecessor PertTasks which are directly connected via a
	 * PertDependency to this CardFigure.
	 */
	public List<CardFigure> getPredecessors() {
		LinkedList<CardFigure> list = new LinkedList<CardFigure>();
		for (DependencyFigure c : getDependencies()) {
			if (c.getEndFigure() == this) {
				list.add((CardFigure) c.getStartFigure());
			}
		}
		return list;
	}

	/**
	 * Returns true, if the current task is a direct or indirect dependent of
	 * the specified task. If the dependency is cyclic, then this method returns
	 * true if <code>this</code> is passed as a parameter and for every other
	 * task in the cycle.
	 */
	public boolean isDependentOf(CardFigure t) {
		if (this == t)
			return true;
		for (CardFigure pre : getPredecessors()) {
			if (pre.isDependentOf(t)) {
				return true;
			}
		}
		return false;
	}

	public String toString() {
		return "CardFigure#" + hashCode() + " " + getNumber() + " "
				+ getDuration() + " " + getDescription();
	}
}
